# -*- coding: utf-8 -*-
"""
日志中间件

记录API请求和响应的详细信息，包括：
- 请求时间和响应时间
- 请求方法和路径
- 请求参数和响应状态
- 用户信息和IP地址
- 错误信息和异常堆栈
"""

import time
import logging
import json
from typing import Callable, Any
from fastapi import Request, Response
from fastapi.routing import Match
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import StreamingResponse
from starlette.types import Receive, Scope, Send
import uuid
import asyncio

logger = logging.getLogger(__name__)


class LoggingMiddleware(BaseHTTPMiddleware):
    """日志中间件"""
    
    def __init__(self, app, skip_paths: list = None):
        super().__init__(app)
        self.skip_paths = skip_paths or ["/health", "/metrics", "/docs", "/redoc", "/openapi.json"]
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """处理请求和响应日志"""
        
        # 跳过不需要记录的路径
        if any(request.url.path.startswith(path) for path in self.skip_paths):
            return await call_next(request)
        
        # 生成请求ID
        request_id = str(uuid.uuid4())
        request.state.request_id = request_id
        
        # 记录请求开始时间
        start_time = time.time()
        
        # 获取客户端IP
        client_ip = self._get_client_ip(request)
        
        # 获取用户信息（如果已认证）
        user_info = self._get_user_info(request)
        
        # 记录请求信息
        await self._log_request(request, request_id, client_ip, user_info)
        
        # 处理请求
        try:
            response = await call_next(request)
            
            # 计算处理时间
            process_time = time.time() - start_time
            
            # 记录响应信息
            await self._log_response(request, response, request_id, process_time)
            
            # 添加响应头
            response.headers["X-Request-ID"] = request_id
            response.headers["X-Process-Time"] = str(round(process_time, 4))
            
            return response
            
        except Exception as e:
            # 计算处理时间
            process_time = time.time() - start_time
            
            # 记录异常信息
            await self._log_exception(request, e, request_id, process_time)
            
            raise
    
    def _get_client_ip(self, request: Request) -> str:
        """获取客户端IP地址"""
        # 优先获取代理传递的真实IP
        if "X-Real-IP" in request.headers:
            return request.headers["X-Real-IP"]
        elif "X-Forwarded-For" in request.headers:
            # X-Forwarded-For可能包含多个IP，取第一个
            return request.headers["X-Forwarded-For"].split(",")[0].strip()
        else:
            # 直连IP
            return request.client.host if request.client else "unknown"
    
    def _get_user_info(self, request: Request) -> dict:
        """获取用户信息"""
        user_info = {"user_id": None, "username": None}
        
        # 尝试从请求状态中获取用户信息
        if hasattr(request.state, "user"):
            user = request.state.user
            if user:
                user_info["user_id"] = getattr(user, "id", None)
                user_info["username"] = getattr(user, "username", None)
        
        return user_info
    
    async def _log_request(self, request: Request, request_id: str, client_ip: str, user_info: dict):
        """记录请求信息"""
        
        # 获取请求体（如果不是文件上传）
        request_body = await self._get_request_body(request)
        
        # 获取查询参数
        query_params = dict(request.query_params)
        
        log_data = {
            "event": "request_start",
            "request_id": request_id,
            "method": request.method,
            "url": str(request.url),
            "path": request.url.path,
            "query_params": query_params,
            "headers": dict(request.headers),
            "client_ip": client_ip,
            "user_agent": request.headers.get("User-Agent", ""),
            "user_info": user_info,
            "body": request_body,
            "timestamp": time.time()
        }
        
        # 敏感信息过滤
        log_data = self._filter_sensitive_data(log_data)
        
        logger.info("API请求开始", extra=log_data)
    
    async def _log_response(self, request: Request, response: Response, request_id: str, process_time: float):
        """记录响应信息"""
        
        log_data = {
            "event": "request_end",
            "request_id": request_id,
            "method": request.method,
            "path": request.url.path,
            "status_code": response.status_code,
            "process_time": round(process_time, 4),
            "response_headers": dict(response.headers),
            "timestamp": time.time()
        }
        
        # 记录响应体（仅限于小的响应）
        if (hasattr(response, "body") and 
            len(response.body) < 1000 and 
            response.status_code >= 400):
            try:
                log_data["response_body"] = response.body.decode("utf-8")
            except:
                pass
        
        # 根据状态码选择日志级别
        if response.status_code >= 500:
            logger.error("API请求完成", extra=log_data)
        elif response.status_code >= 400:
            logger.warning("API请求完成", extra=log_data)
        else:
            logger.info("API请求完成", extra=log_data)
    
    async def _log_exception(self, request: Request, exception: Exception, request_id: str, process_time: float):
        """记录异常信息"""
        
        log_data = {
            "event": "request_error",
            "request_id": request_id,
            "method": request.method,
            "path": request.url.path,
            "exception_type": type(exception).__name__,
            "exception_message": str(exception),
            "process_time": round(process_time, 4),
            "timestamp": time.time()
        }
        
        logger.error("API请求异常", extra=log_data, exc_info=True)
    
    async def _get_request_body(self, request: Request) -> str:
        """获取请求体信息 - 不读取实际内容避免流消耗问题"""
        try:
            # 只记录内容类型，不读取实际请求体内容
            content_type = request.headers.get("Content-Type", "")
            content_length = request.headers.get("Content-Length", "0")
            
            if "multipart/form-data" in content_type:
                return f"[multipart form data - {content_length} bytes]"
            elif "application/json" in content_type:
                return f"[json data - {content_length} bytes]"
            elif "application/x-www-form-urlencoded" in content_type:
                return f"[form data - {content_length} bytes]"
            elif content_type:
                return f"[{content_type} - {content_length} bytes]"
            else:
                return f"[request body - {content_length} bytes]"
            
        except Exception as e:
            logger.debug(f"获取请求体信息失败: {e}")
            return "[body info unavailable]"
    
    def _filter_sensitive_data(self, log_data: dict) -> dict:
        """过滤敏感数据"""
        sensitive_fields = {
            "password", "passwd", "secret", "token", "key", "authorization",
            "x-api-key", "cookie", "session", "csrf"
        }
        
        def filter_dict(data):
            if isinstance(data, dict):
                filtered = {}
                for key, value in data.items():
                    if any(field in key.lower() for field in sensitive_fields):
                        filtered[key] = "[FILTERED]"
                    else:
                        filtered[key] = filter_dict(value)
                return filtered
            elif isinstance(data, list):
                return [filter_dict(item) for item in data]
            else:
                return data
        
        return filter_dict(log_data)